package GUI;

import java.awt.*;
import java.awt.event.*;
import java.util.*;

import GUI_Element.*;
import Kernel.*;

import javax.swing.*;


public class PN_Draw_Panel extends JPanel implements MouseListener, MouseMotionListener{

	private static final long serialVersionUID = 5935390986480675664L;
	
	/* The state of pointer. It can be changed by PN_Pointer_Panel. */
	private int DrawState = DS_SELECT;
	public static final int DS_SELECT = 0;
	public static final int DS_PLACE = 1;
	public static final int DS_TRANSITION = 2;
	public static final int DS_ARC = 3;
	public static final int DS_TOKEN = 4;
	public static final int DS_ERASER = 5;
	public static final int DS_DRAGGING = 6;
	
	/* A Set containing all the elements */
	AbstractSet<Element> Elements = new HashSet<Element>();
	
	/* The Element point is now point to. Or saying the validated Element */
	private Element elemNow = null;
	private Element elemFore = null;
	
	/* Structure storing the net graph */
	private Net net = new Net();
	
	private JFrame MainFrame;
	
	public PN_Draw_Panel(JFrame MainFrame){
		this.setPreferredSize(new Dimension(2000,2000));
		this.setBackground(Color.white);
		addMouseMotionListener(this);
		addMouseListener(this);
		this.MainFrame = MainFrame;
	}
	
	public void SetDrawState(int state) {
//		System.out.println("State has been changed to "+state);
		this.DrawState = state;
	}

	public void AddElement(Element e){
		Elements.add(e);
	}
	
	public void mouseDoubleClicked(MouseEvent e){
		Point now = e.getPoint();
		switch(this.DrawState) {
		case(DS_PLACE):
		case(DS_TOKEN):
			if ( !testCursorInObject(now.x, now.y) || !(elemNow.GetType()==Element.PLACE) )
				break;
			String tokenInput = JOptionPane.showInputDialog(MainFrame, 
					"Please Input the number of Tokens",
					"Input Dialog",
					JOptionPane.INFORMATION_MESSAGE);
			if (tokenInput == null)
				return;
			((ShapePlace)elemNow).GetActual().SetToken(new Integer(tokenInput).intValue());
			break;
		case(DS_ARC):
			if ( !testCursorInObject(now.x, now.y) || 
					!(elemNow.GetType()==Element.ARCP2T || elemNow.GetType()==Element.ARCT2P))
				break;
			String weightInput = JOptionPane.showInputDialog(MainFrame,
					"Please Input the weight of the Arc",
					"Input Dialog",
					JOptionPane.PLAIN_MESSAGE);
			if (weightInput == null)
				return;
			((ShapeArc)elemNow).GetActual().SetWeight(new Integer(weightInput).longValue());
			break;
		case(DS_ERASER):
			if ( !testCursorInObject(now.x, now.y) )
				break;
			elemFore = null;
			DeleteElement(elemNow);
			break;
		}
		repaint();
	}
	
	public void mouseClicked(MouseEvent e) {
		if (e.getClickCount() == 2) {
//			System.out.println("Double Click");
			mouseDoubleClicked(e);
			return;
		}
		Point now = e.getPoint();
		switch(this.DrawState) {
		case(DS_SELECT):
			break;
		case(DS_PLACE):
			ShapePlace sp = new ShapePlace(now.x, now.y);
			Elements.add(sp);
//			net.Register(sp.GetActual());
			break;
		case(DS_TRANSITION):
			ShapeTransition st = new ShapeTransition(now.x, now.y);
			Elements.add(st);
//			net.Register(st.GetActual());
			break;
		case(DS_ARC):
			if (elemFore == null) {
				if (!testCursorInObject(now.x, now.y)
						|| elemNow.GetType() == Element.ARCP2T
						|| elemNow.GetType() == Element.ARCT2P )
					break;
				elemFore = elemNow;
				elemFore.SetSelected(true);
			} else {
				if (!testCursorInObject(now.x, now.y)
						|| elemNow.GetType() == Element.ARCP2T
						|| elemNow.GetType() == Element.ARCT2P )
					break;
				if (elemFore == elemNow) {
					elemFore.SetSelected(false);
					elemFore = null;
					break;
				}
				if (elemFore.GetType() == Element.PLACE
						&& elemNow.GetType() == Element.TRANSITION ) {
//					System.out.println("p" + ((ShapePlace)elemFore).PlaceIndex + " ,t" + ((ShapeTransition)elemNow).TransitionIndex);
					ShapeArc sa = new ShapeArc(ShapeArc.TYPEP2T, (ShapePlace)elemFore, (ShapeTransition)elemNow);
					Elements.add(sa);
					net.Register((Arc_P2T)sa.GetActual());
					elemFore.SetSelected(false);
					elemFore = null;
					break;
				}
				if (elemFore.GetType() == Element.TRANSITION
						&& elemNow.GetType() == Element.PLACE ) {
					ShapeArc sa = new ShapeArc(ShapeArc.TYPET2P, (ShapePlace)elemNow, (ShapeTransition)elemFore);
					Elements.add(sa);
					net.Register((Arc_T2P)sa.GetActual());
					elemFore.SetSelected(false);
					elemFore = null;
					break;
				}	
			}
			break;
		case(DS_TOKEN):
			if (testCursorInObject(now.x, now.y)
					&& elemNow.GetType() == Element.PLACE )
				((ShapePlace)elemNow).IncrementToken();
			break;
		case(DS_ERASER):
			if ( !testCursorInObject(now.x, now.y) )
				break;
			elemFore = null;
			DeleteElement(elemNow);
			break;
		}
		repaint();
	}
	
	public void Register(Element e){
		switch (e.GetType()){
		case (Element.ARCP2T):
			net.Register((Arc_P2T)((ShapeArc)e).GetActual());
			break;
		case (Element.ARCT2P):
			net.Register((Arc_T2P)((ShapeArc)e).GetActual());
			break;
		default:
			throw new RuntimeException();
		}
	}

	private void DeleteElement(Element e) {
		if ( e.GetType() == Element.ARCP2T ) {
			ShapeArc sa = (ShapeArc)e;
			sa.GetPlace().RemovePost(sa);
			sa.GetTransition().RemovePre(sa);
			net.UnRegister((Arc_P2T)sa.GetActual());
		} else if ( e.GetType() == Element.ARCT2P ) {
			ShapeArc sa = (ShapeArc)e;
			sa.GetPlace().RemovePre(sa);
			sa.GetTransition().RemovePost(sa);
			net.UnRegister((Arc_T2P)sa.GetActual());
		} else if ( e.GetType() == Element.PLACE) {
			ShapePlace place = (ShapePlace)e;
			for ( ShapeArc sa : place.GetPostArcSet() ) {
				sa.GetTransition().RemovePre(sa);
				net.UnRegister((Arc_P2T)sa.GetActual());
				Elements.remove(sa);
			}
			for ( ShapeArc sa : place.GetPreArcSet() ) {
				sa.GetTransition().RemovePost(sa);
				net.UnRegister((Arc_T2P)sa.GetActual());
				Elements.remove(sa);
			}
			place.GetPostArcSet().clear();
			place.GetPreArcSet().clear();
			net.UnRegister(place.GetActual());
		} else if ( e.GetType() == Element.TRANSITION ) {
			ShapeTransition transition = (ShapeTransition)e;
			for ( ShapeArc sa : transition.GetPostArcSet()) {
				sa.GetPlace().RemovePre(sa);
				net.UnRegister((Arc_T2P)sa.GetActual());
				Elements.remove(sa);
			}
			for ( ShapeArc sa : transition.GetPreArcSet()) {
				sa.GetPlace().RemovePost(sa);
				net.UnRegister((Arc_P2T)sa.GetActual());
				Elements.remove(sa);
			}
			transition.GetPreArcSet().clear();
			transition.GetPostArcSet().clear();
			net.UnRegister(transition.GetActual());
		}
		Elements.remove(e);
	}
	
	public void mouseEntered(MouseEvent e) {}

	public void mouseExited(MouseEvent e) {}

	public void mousePressed(MouseEvent e) {
		Point now = e.getPoint();
		if ( DrawState == DS_SELECT 
				&& testCursorInObject(now.x, now.y)
				&& elemNow.GetType() != Element.ARCP2T
				&& elemNow.GetType() != Element.ARCT2P)
			DrawState = DS_DRAGGING;
	}

	public void mouseReleased(MouseEvent e) {
		if ( DrawState == DS_DRAGGING)
			DrawState = DS_SELECT;
	}

	public void mouseDragged(MouseEvent e) {
		Point now = e.getPoint();
		if ( DrawState != DS_DRAGGING ) 
			return;
		elemNow.SetPosition(now.x, now.y);
	}

	public void mouseMoved(MouseEvent e) {
		if ( DrawState != DS_ERASER )
			return;
		Point now = e.getPoint();
		if ( !this.testCursorInObject(now.x, now.y) ) {
			if ( elemFore != null ){
				this.SetElementDelete(elemFore, false);
				elemFore = null;
			}
			return;
		}
		if (elemNow != elemFore) {
			if (elemFore != null)
				this.SetElementDelete(elemFore, false);
			elemFore = elemNow;
			this.SetElementDelete(elemNow, true);
		}
	}
	
	private void SetElementDelete(Element e, boolean b) {
		if ( e.GetType() == Element.ARCP2T || e.GetType() == Element.ARCT2P )
			e.SetToBeDeleted(b);
		else if ( e.GetType() == Element.PLACE ){
			ShapePlace place = (ShapePlace)e;
			place.SetToBeDeleted(b);
			for ( ShapeArc sa : place.GetPreArcSet() )
				sa.SetToBeDeleted(b);
			for ( ShapeArc sa : place.GetPostArcSet() )
				sa.SetToBeDeleted(b);
		} else if ( e.GetType() == Element.TRANSITION ){
			ShapeTransition transition = (ShapeTransition)e;
			transition.SetToBeDeleted(b);
			for ( ShapeArc sa : transition.GetPreArcSet() )
				sa.SetToBeDeleted(b);
			for ( ShapeArc sa : transition.GetPostArcSet() )
				sa.SetToBeDeleted(b);			
		} else
			throw new RuntimeException();
	}
	
	public void paint(Graphics g) {
		super.paint(g);
		Graphics2D g2d = (Graphics2D)g;
		
		for(Element e: Elements){
			e.draw(g2d);
		}
	}
	
	public boolean testCursorInObject(int nowX,int nowY){
		if(Elements.size()==0)
			return false;
		
		for (Element e : Elements) {
			if ( e.GetType() != Element.ARCP2T 
					&& e.GetType() != Element.ARCT2P
					&& e.shape.contains(nowX, nowY)) {
				elemNow = e;
				return true;
			} else if ( (e.GetType() == Element.ARCP2T || e.GetType() == Element.ARCT2P)
					&& ((ShapeArc)e).OnLine((float)nowX, (float)nowY) ){
				elemNow = e;
				return true;
			}
		}
		return false;
	}

	public void Start(){
		net.Start();
	}
	public void Suspend(){
		net.Suspend();
	}
	
	public void SetOneStep() {
		net.SetOneStep();
	}
	
	public void SetInterval(int min, int max) {
		Transition.SetInterval(min, max);
	}
	
	public void CleanAllElement(){
		while (!Elements.isEmpty())
			DeleteElement(Elements.iterator().next());
	}
	
	public void CheckLivenessOfEFCN() {
		if (!net.CheckEFCN()) {
			JOptionPane.showMessageDialog(MainFrame, "This is not a extended Free-Choice Net!",
					"Wrong Request", JOptionPane.ERROR_MESSAGE);
			return;
		}
		String liveness = (net.CheckLivenessOfEFCN()? "live":"dead");
		JOptionPane.showMessageDialog(MainFrame, "This EFCN is " + liveness + "!",
				"Check Liveness", JOptionPane.INFORMATION_MESSAGE);
		
	}
}
